/*!
    @class Car
    @brief Obiekt samochodu poruszający się po rondzie

    Samochód jest w pełni samodzielnym obiektem, tylko on może decydować o swoich ruchach.
    Samochód oczekuje, że zostanie stworzony na 'odpowiednim' pasie gałęzi startowej, zgodnej z jego zestawem zasad.
    Cel samochodu (docelowa gałąź i pas) są przechowywane w _dest i _destLane.
    Prędkość jest opisana trzema zmiennymi:
    <ul>
    <li> _speed - chwilowa prędkość, o tyle pikseli zostanie przesunięty wóz w najbliższym wywołaniu advance().
    <li> _minSpeed - prędkość minimalna, do której samochód może wyhamować w brake(). Ta zmienna jest ustawiana tylko w trakcie zmiany pasa oraz w stanie irytacji.
    <li> _maxSpeed - zmienna określająca jak szybko może jechać dany samochód; nie zmieniana po stworzeniu samochodu. Samochód powinien dążyć do tej prędkości, gdy tylko może.
    </ul>

    Zasady mogą być jedne z trzech: official, matchingLanes oraz invertedPriority. Wszystkie
    trzy mają stworzone dla siebie osobne funkcje i są zbudowane wokół tego samego szablonu.
    Najpierw jest potężny if, który na podstawie zmiennych _onRim i _onBranch stwierdza, czy
    samochód jest już na rondzie, czy na gałęzi. Następnie, samochód może wykonywać kilka
    operacji, wyliczonych przez enum \link types.h::Manouver Manouver \endlink.

    Decyzje co do zmiany stanu (i/lub położenia) zapadają na podstawie interakcji z
    innymi obiektami na scenie (a w szczególności z innymi samochodami,
    z barierami oraz z pieszymi).
    Podczas jazdy samochód ma przed sobą krótki 'szperacz' (lekko odchylony na prawo,
    aby nie zawadzać o sąsiednie pasy przy skręcaniu). Jeżeli szperacz wykryje przed
    sobą jakiś widoczny obiekt (pieszy lub samochód), samochód wyhamuje do swojej
    prędkości minimalnej.
    Wielokrotnie podczas jazdy będzie tworzony obietk QPainterPath i przypisywany do domyślnie
    pustej _dangerZone. Obiekt ten określa strefę szczególnie istotną podczas wykonywania
    aktualnego manewru. Na przykład podczas wjeżdżania na rondo powstanie _dangerZone obejmująca
    obszar na lewo od samochodu, sięgająca tego pasa, na który zamierza wjechać. Tak długo,
    jak w tej strefie zagrożenia będzie jakiś obiekt (zakładam, że samochód), tak długo będziemy
    stać, aby go przepuścić. Strefa Zagrożenia jest stosowana właśnie w celu zasymulowania
    wzajemnego przepuszczania się samochodów w ramach określonego pierwszeństwa.

    Podczas jazdy samochód sprawdza, czy nie najechał na którąś z barier (ich rodzaje są zdefiniowane w enumie \link types.h::BarrierType BarrierType\endlink.
    'Oczywiście' w danej chwili samochód sprawdza tylko te bariery, które go interesują, to znaczy, że np. bariera zmiany pasa zostanie zignorowana podczas zjeżdżania z ronda.
    O odpowiednie rozmieszczenie barrier na rondzie dba klasa QRoundabout.

    Jeżeli samochód będzie stał (lub poruszał się z minimalną prędkością) zbyt długo, kierowca się zirytuje i zacznie deptać po gazie. Może to brzmi fajnie, ale jest to mechanizm zapobiegający zakleszczeniom (sytuacjom, w którym dwa samochody najadą na siebie, dopiero wtedy się wykryją i zatrzymają. Mimo że takich sytuacji być nie powinno, nie sposób się przed nimi ustrzec. Jeżeli zatem będziemy stać zbyt długo, samochód zacznie się wreszcie trochę poruszać. Po więcej szczegółów zobacz irritationTick() i advance().

    */

#include "qroundabout.h"
#include "pedestrian.h"
#include <QGraphicsScene>
#include <QPainter>
#include <QTimer>
#include <QStyleOption>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <math.h>

#define PI 3.14159265358979323846264338327950288419716939937510
/**
@brief Jedyny konstruktor

Inicjalizacja składowych klasy, ustawienie samochodu w określonym punkcie na scenie.
@param pos Współrzędne (sceny) na które zostanie przesunięty wóz w momencie stworzenia
@param angle Rotacja samochodu, względem jego środka. Ta zmienna na początku przechowuje wartość w stopniach, po wjeździe na rondo jest przeliczane na radiany.
@param laneWidth W zależności od tej wartości wyliczam długość i szerokość samochodu.
@param qr Stanowi odniesienie do obiektu ronda, do którego należymy. Wykorzystywany do pobrania informacji na temat n-tej gałęzi, zależności między nimi, etc.
@param branchlane Pas jezdni, na którym zaczynamy jazdę
@param dest Numer gałęzi, na którą jedziemy
@param source Numer gałęzi, z której jedziemy
@param destLane Pas na docelowej gałęzi, na który mamy zjechać
@param rules Identyfikator zasad; dawniej był opisowy, ale powodowało to problemy na niektórych komputerach. 0 - oficjalnie, 1 - matchingLanes, 2 - invertedPriority.
*/
Car::Car(QPointF pos, qreal angle, qreal laneWidth, QRoundabout *qr, int branchlane, short int dest, int source, short int destLane, int rules)
    : _size(laneWidth - laneWidth/3 ),  _rules(rules),
      _destination(dest), _source(source), _destLane(destLane),
      _angle(angle), _speed(0), _minSpeed(0), _maxSpeed(4), _dangerZone(NULL),
      _lane(branchlane), _state(forward), _disabled(false),
      _shortcutUsed(false), _zebraDangerZone(false), _destinationReached(false),
      _color(qrand() % 256, qrand() % 256, qrand() % 256),
      _qr(qr), _stopLine(NULL)
{
    setPos(mapToParent(pos));
    setRotation(_angle);
    _onBranch = true;
    _onRim = false;
    _laneShift = 0;
    _secondsOfIrritation = 0;

    _destBarrier = findOutletBarrier(_destination);

     _irritationTimer = new QTimer(this);
     //connect(_irritationTimer, SIGNAL(timeout()), this, SLOT(irritationTick()));

    _car = new QRectF(-_size/2, -_size, _size, 2*_size);  // żeby punkt (0.0) był w środku wozu
    upperLeft = QPointF(-_size/2, -_size);
    upperRight = QPointF(+_size/2, -_size);
    bottomRight = QPointF(+_size/2, +_size);
    bottomLeft = QPointF(-_size/2, +_size);

    _speed = 0;

    if (_rules == 0)
        _color = Qt::blue;
    if (_rules == 1)
        _color = Qt::red;
    if (_rules == 2)
        _color = Qt::yellow;

    setAcceptsHoverEvents(true);
    //setCacheMode(QGraphicsItem::ItemCoordinateCache); // TODO: enable to w release
}
/*! Funkcja ograniczająca obszar po którym malujemy. Zwiększenie tego obszaru spowalnia aplikację.
@see dokumentacja Qt*/
QRectF Car::boundingRect() const
{
//    return QRectF(-2000, -2000, 4000, 4000);  // to się przydaje, jak chcesz narysować coś poza prostokątem samochodu
//    return QRectF(-350, -350, 700, 700);
//    return QRectF(-100, -100, 200, 200);
    return *_car;
}

/** @see dokumentacja Qt*/
QPainterPath Car::shape() const
{
    QPainterPath path;
    path.addRect(*_car);
    return path;
}
/*! Rysowanie samochodu z chromowanym zderzakiem z przodu dla zaznaczenia kierunku jazdy.
*/
void Car::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
{
    painter->setClipRect( option->exposedRect );
    QBrush brush(Qt::SolidPattern);
    brush.setColor(_color);
    painter->setBrush(brush);
    painter->drawRect(*_car);

    brush.setColor(Qt::white);
    QPen pen(brush, 2);
    pen.setColor(Qt::white);
    painter->setPen(pen);
    painter->drawLine(QLineF(-_size/2, -_size, _size/2, -_size));
//    painter->drawLine(QLineF(mapFromParent(0, 0), QPointF(0, 0)));

    // szperacz z goForward
//    QPainterPath path(QPointF(0, -_qr->getLaneWidth()));
//    path.lineTo(+_size/2, -1.5 * _qr->getLaneWidth());
//    path.lineTo(+_size/2, -_qr->getLaneWidth());
//    painter->drawPath(path);

    if (_disabled)
    {
        pen.setColor(Qt::black);
        painter->setPen(pen);
        painter->drawLine(QLineF(-_size/2, -_size, _size/2, _size));
        painter->drawLine(QLineF(+_size/2, -_size, -_size/2, _size));
    }

}


/*!
  \brief Funkcja wywoływana, gdy scena żąda przesunięcia obiektu, przeładowana z QGraphicsItem.

  Wybieramy tu funkcję odpowiadającą naszym zasadom, sprawdzamy też czy samochód nie powinien być usunięty.
  Jeżeli poruszamy się zbyt wolno na rondzie, zostaje uruchomiony _irritationTimer.
*/
void Car::advance(int step)
{
    if (!step)
        return;
    if (_disabled)
        return;

    if (_rules == 0)
        official();
    else if (_rules == 1)
            matchingLanes();
        else if (_rules == 2)
            invertedPriority();
        else
            official();

//    if ((_speed < _minSpeed + 0.1) && (_irritationTimer->isActive() == false) && (_onRim == true))
//        _irritationTimer->start(1000);

    if (QLineF(QPointF(0,0), pos()).length() > _qr->getRadius() + 3 * _qr->getLaneWidth())
    if ((_destinationReached) &&
         ( QLineF( QPointF(0,0), pos() ).length() > _qr->getRadius() + 15 * _qr->getLaneWidth()))
    {
        this->~Car();
        return;
    }
}

/*!
  \brief Event połączony z tykaniem _irritationTimer'a.

  \link Car::_secondsOfIrritation _secondsOfIrritation \endlink oznacza poziom irytacji.
  Jeśli jedziemy zbyt wolno, wzrasta poziom irytacji.
  Jeśli jedziemy szybciej od prędkości minimalnej, poziom irytacji opada.
  Poziom irytacji może przekroczyć pewną wartość, zależną od stanu samochodu. Najwolniej zirytują się kierowcy jadący na wprost po rondzie, potem zmieniający pas, a najszybciej: skręcający. Takie stopniowanie powinno w teorii zapobiec sytuacji, gdy dwa zakleszczone samochody irytują się w tym samym momencie, jadą kawałek i zakleszczają się znowu; zakleszczenia zachodzą najczęściej między samochodami w różnych stanach.
  W stanie irytacji stopniowo zwiększana jest prędkość samochodu (przez podniesienie _minSpeed oraz usuwana jest _dangerZone, aby nic nie mogło zatrzymać samochodu.
*/
void Car::irritationTick()
{
    //qDebug() << "irritationTick()" << _secondsOfIrritation << _speed << _dangerZone;
    if (_speed <= _minSpeed)
        _secondsOfIrritation++;
    else
    {
        _secondsOfIrritation -= 1;
        if (_secondsOfIrritation <= 0)
        {
            _irritationTimer->stop();
            _secondsOfIrritation = 0;
            _minSpeed = 0;  // już mi przeszło
        }
    }

    if (_secondsOfIrritation >= (12 + (8- 4*int(_state)))) // forward: 20. Turning: 16. Changing: 12.
    {
        if (_minSpeed < 3)
            _minSpeed += 0.1;
        brake();  // zwiększyiśmy _minSpeed, więc brake nas przyspieszy
        if (_dangerZone != NULL)
        {
            delete _dangerZone;
            _dangerZone = NULL;
        }
    }
}

/**
    \brief Zasady oficjalne.

    Zjazd z ronda tylko z zewnętrznego pasa obręczy ronda. Można zjechać na dowolny pas gałęzi.
    Jadąc prawym pasem można nie zjechać z ronda.
    Wjeżdżając na rodno wybieram pas korelujący z pasem gałęzi na którym dojechałem do ronda */
void Car::official()
{
    if (_onBranch)
    {
        switch (_state)
        {
            case forward:
            {
                Barrier *barrier = collidesWithBarrier(enterroundabout);
                if (barrier != NULL)
                {   //zaczynam wjeżdżać na rondo
                   _state = turning; //liczymy, na który pas na rondzie chcemy wjechać
                   int diff = minusModulo(_source, _destination, _qr->getBranches()->size());
                   // diff to różnica. Różnica = 1 oznacza, że gałęzie są obok siebie
                   // ergo, pas winien być = 0
                   diff--;

                   // okej... a teraz chciałbym nie liczyć wjazdów...
                   if (_lane > 0)
                   {
                       int i = _source + 1;
                       while (i != _destination)
                       { // lecę po wszystkich gałęziach między nami i odejmuję 1 za każdy wjazd między nami
                           if (i ==  _qr->getBranches()->size())
                               i = 0;
                           if ( _qr->getBranches()->at(i)->getDirection() == entry)
                               diff--;
                           i++;
                       }
                   }

                   diff = (diff < _lane ? _lane : diff); // nie mogę wjechać na pas mniejszy niż na którym jestem obecnie

                   while (diff >= _qr->getNumLanes())
                       diff--;
                   _lane = diff;
                   prepareStopLine(_lane, barrier); // wjezdżając na dalszy pas ronda, najpierw jadę do przodu, potem skręcam. Muszę wiedzieć, kiedy się zatrzymać.
                }
                else // jazda do przodu na gałęzi
                {
                    goForward();
                    if ((minusModulo(_source, _destination, _qr->getBranches()->size()) == 1) &&
                        (_qr->getDependences()->at(_destination) == connected))
                    {
                        if ((collidesWithBarrier(shortcut) != NULL) && (_lane == 0))
                        {
                            _state = turning;
                            _lane = -2;
                        }
                    }
                }
                break;
            }
            case turning:
                enterRoundabout(); // pas został wybrany przy wywołaniu prepareStopLine();
                if ((abs(int(_angle - rotation())) >= 65) && (collidesWithBarrier(enterroundabout) == NULL))
                {
                    _angle = computeAngle();   // ustawiam prawidłowy kąt (żegnaj _angle w stopniach...)
                    //qDebug() << "r() = " << rotation() << "angle = " << _angle*180.0/Pi;
                    _state = forward;
                    _onRim = true;
                    _onBranch = false;
                }
                break;
            case changing: // nie powinno się zdarzyć onBranch;
                break;
        }
    }

    if (_onRim)//jedzie w kółko
    {
        switch (_state)
        {
            case forward:
            {
                orbit(_qr->getRadius() - ((_qr->getLaneWidth())/2)*(((_lane)*2)+1) );
                if (_lane > 0)
                {
                    Barrier *barrier = collidesWithBarrier(changelane);
                    if (barrier != NULL)
                    {
                        // rozważam, czy chcę zmienić pas. Jeśli tak to zmieniam stan na zmiana pasu
                        if ((_lane + _destination) % _qr->getBranches()->size() == barrier->getNrBranch())
                        {
                            _state = changing;
                            createChangeLaneDangerZone();
                            return;
                        }
                    }
                }
                Barrier *barrier = collidesWithBarrier(exitroundabout);
                if ((barrier == _destBarrier) && (_lane == 0)) // zjeżdżać można tylko z zewnętrznego pasa!
                {
                    _state = turning;
                    prepareStopLine(_destLane + 0.1, barrier);
                    _angle = computeAngle(); // zapamiętujemy nasz kąt z tuż przed skręcania
                    // żeby łatwiej wykryć kiedy mamy przestać skręcać
                    return;
                }
                barrier = collidesWithBarrier(shortcutend);
                if (barrier != NULL)
                {
                    createShortcutEndDangerZone(_destination);
                    _state = turning;
                    _angle = computeAngle(); // zapamiętujemy nasz kąt z tuż przed skręcania
                    // żeby łatwiej wykryć kiedy mamy przestać skręcać
                    _shortcutUsed = true;
                }
                break;
            }
            case changing:
                if (changeLane())
                    _state = forward;
                break;
            case turning:
                int prev = minusModulo(_destination, 1, _qr->getBranches()->size());
                if (turnExit())
                {  // skręcanie zakończone - ustalmy poprawną pozycję
                    setRotation(_qr->getBranches()->at(_destination)->getFi() - 180);
                    qreal radius = QLineF(mapFromParent(0, 0), QPointF(0, 0)).length();
                    setPos(radius * cos((1.5 * Pi) - ((360-rotation())*Pi/180)), radius * sin((1.5 * Pi)  - ((360 - rotation())*Pi/180))); //ustawienie na środku gałęzi

                    if (_qr->getDependences()->at(prev) == glued)
                    { // poprzednia jest ze mną sklejona, muszę przesunąć się o jej pół szerokości w prawo
                        setPos(mapToParent(_qr->getBranches()->at(prev)->getWidth()/2, 0));
                    }
                    qreal myLane = _lane < 0 ? 0 : _destLane;   // jeżeli jadę skrótem, to wjadę na zerowy pas.
                    // przesuwam się najpierw na zerowy pas...
                    setPos(mapToParent(_qr->getLaneWidth() * (0.5* _qr->getBranches()->at(_destination)->getNumLanes() - 0.5), 0));
                    setPos(mapToParent(-myLane * _qr->getLaneWidth(), 0)); // a teraz na mój wybrany

                    _state = forward;
                    _onRim = false;
                    _onBranch = true;
                    _destinationReached = true;
                }
                break;
        }
    }
}

/** Zasady tak jak w matchingLanes(), ale samochody jadące po obręczy przepuszczają zjeżdżających z ronda (z lewej strony) */
void Car::invertedPriority()
{
    if (_onBranch)
    {
        switch (_state)
        {
            case forward:
            {
                Barrier *barrier = collidesWithBarrier(enterroundabout);
                if (barrier != NULL)
                {   //zaczynam wjeżdżać na rondo
                    _state = turning;
                    /*
                       Wóz może wjechać na każdy pas między minimalnym (czyli destLane)
                       a maksymalnym (ilość barier zmian pasów pozostała do celu - destLane)
                       ALE! nie może wjechać na pas większy, niż na którym jest na gałęzi (o ile jest na maksymalnym pasie gałęzi)
                       */

                    int barriersLeft = minusModulo(_source, _destination, _qr->getBranches()->size() ); // liczba pozostałych okazji do zmiany pasa
                    int minimalLane = ((_destLane < _lane) ? _lane : _destLane); // wierzę, że RSim::generator nie wpuści mnie na pas 0 z destLane == 1;
                    int maximalLane = barriersLeft + _destLane;

                    maximalLane = (maximalLane >= _qr->getNumLanes() ? _qr->getNumLanes()-1 : maximalLane);
                    if (_lane < _qr->getNumLanesOnBranch(barrier->getNrBranch())-1 ) // jeśli nie jestem na zewnętrznym pasie
                        maximalLane = minimalLane;

                    int diff = minusModulo(_source, _destination, _qr->getBranches()->size());
                    // diff to różnica. Różnica = 1 oznacza, że gałęzie są obok siebie
                    if (diff == 1)
                        maximalLane = minimalLane;

                    if (minimalLane >= maximalLane)
                        _lane = minimalLane;
                    else
                        _lane = minimalLane + (qrand() % (maximalLane - minimalLane + 1));

                    prepareStopLine(_lane, barrier); // wjezdżając na dalszy pas ronda, najpierw jadę do przodu, potem skręcam. Muszę wiedzieć, kiedy się zatrzymać.
                }
                else // jazda do przodu na gałęzi
                {
                   goForward();
                   if ((minusModulo(_source, _destination, _qr->getBranches()->size()) == 1) &&
                       (_qr->getDependences()->at(_destination) == connected))
                   {
                       if ((collidesWithBarrier(shortcut) != NULL) && (_lane == 0))
                       {
                           _state = turning;
                           _lane = -2;
                       }
                   }

                }
                break;
            }
            case turning:
                enterRoundabout(); // pas został wybrany przy wywołaniu prepareStopLine();
                if ((abs(int(_angle - rotation())) >= 65) && (collidesWithBarrier(enterroundabout) == NULL))
                {
                    _angle = computeAngle();   // ustawiam prawidłowy kąt (żegnaj _angle w stopniach...)
                    //qDebug() << "r() = " << rotation() << "angle = " << _angle*180.0/Pi;
                    _state = forward;
                    _onRim = true;
                    _onBranch = false;
                }
                break;
            case changing: // nie powinno się zdarzyć onBranch;
                break;
        }
    }

    if (_onRim)//jedzie w kółko
    {
        switch (_state)
        {
            case forward:
            {
                orbit(_qr->getRadius() - ((_qr->getLaneWidth())/2)*(((_lane)*2)+1) );
                if (_lane > _destLane)
                {
                    Barrier *barrier = collidesWithBarrier(changelane);
                    if ((barrier != NULL) && (_lane > _destLane))  // jeśli lane == destLane, to z tego pasu chcę już zjechać.
                    {
                        /// rozważam, czy chcę zmienić pas.
                        int barriersLeft = minusModulo(barrier->getNrBranch(), _destination + 1, _qr->getBranches()->size()); // liczba pozostałych okazji do zmiany pasa
                        // +1, bo bariera powiązana z celemPodróży wcale nie będzie szansą na zmianę pasa (jest już za zjazdem)

                        // trzeba jeszcze odjąć sklejone gałęzie po drodze
                        // bo każde sklejenie to jedna okazja mniej do zmiany pasa
                        for (int i = barrier->getNrBranch(); i != _destination; i = i)
                        {
                            if (_qr->getDependences()->at(i) == glued)
                                barriersLeft--;
                            i = minusModulo(i, 1, _qr->getBranches()->size());
                        }

                        if (_lane - _destLane == barriersLeft + 1) // bo barriersLeft nie uwzględnia tej bariery na której jestem
                        {   // to moja ostatania szansa na zmianę pasa - trzeba wykorzystać
                            _state = changing;
                            createChangeLaneDangerZone();
                            return;
                        }
                        else
                        {   // nie muszę zmienić pasa, ale mogę to zrobić - losujemy.
                            int tmp = rand()%35;
                            if (tmp == 0)
                            {
                                _state = changing;
                                createChangeLaneDangerZone();
                            }
                        }
                    }
                }
                Barrier *barrier = collidesWithBarrier(exitroundabout);
                if ((barrier == _destBarrier) && (_lane < _qr->getNumLanesOnBranch(barrier->getNrBranch())))
                {
                    _state = turning;
                    prepareStopLine(_lane + 0.1, barrier);
                    _angle = computeAngle(); // zapamiętujemy nasz kąt z tuż przed skręcania
                    // żeby łatwiej wykryć kiedy mamy przestać skręcać
                    return;
                }
                barrier = collidesWithBarrier(shortcutend);
                if (barrier != NULL)
                {
                    createShortcutEndDangerZone(_destination);
                    _state = turning;
                    _angle = computeAngle(); // zapamiętujemy nasz kąt z tuż przed skręcania
                    // żeby łatwiej wykryć kiedy mamy przestać skręcać
                    _shortcutUsed = true;
//                    _destLane = 0;
                }
                break;
            }
            case changing:
                if (changeLane())
                    _state = forward;
                break;
            case turning:
                int prev = minusModulo(_destination, 1, _qr->getBranches()->size());
                if (turnExit())
                {
                    Branch *destBranch = _qr->getBranches()->at(_destination);

                    setRotation(destBranch->getFi() - 180);
                    qreal radius = QLineF(mapFromParent(0, 0), QPointF(0, 0)).length();
                    setPos(radius * cos((1.5 * Pi) - ((360-rotation())*Pi/180)), radius * sin((1.5 * Pi)  - ((360 - rotation())*Pi/180))); //ustawienie na środku gałęzi

                    if (_qr->getDependences()->at(prev) == glued)
                    { // poprzednia jest ze mną sklejona, muszę przesunąć się o jej pół szerokości w prawo
                        setPos(mapToParent(_qr->getBranches()->at(prev)->getWidth()/2, 0));
                    }
                    qreal myLane = _lane < 0 ? 0 : _lane;
                    // przesuwam się najpierw na zerowy pas...
                    setPos(mapToParent(_qr->getLaneWidth() * (0.5* _qr->getBranches()->at(_destination)->getNumLanes() - 0.5), 0));
                    setPos(mapToParent(-myLane * _qr->getLaneWidth(), 0)); // a teraz na mój wybrany

                    _state = forward;
                    _onRim = false;
                    _onBranch = true;
                    _destinationReached = true;
                }
                break;
        }
    }

}

/**
    Zjeżdżając z ronda można zjechać tylko na pas odpowiadający pasowi ronda.
    Można nie zjeżdżać z ronda.
 */
void Car::matchingLanes()
{
    if (_onBranch)
    {
        switch (_state)
        {
            case forward:
            {
                Barrier *barrier = collidesWithBarrier(enterroundabout);
                if (barrier != NULL)
                {   //zaczynam wjeżdżać na rondo
                    _state = turning; //liczymy, na który pas na rondzie chcemy wjechać

                    /*
                       Wóz może wjechać na każdy pas między minimalnym (czyli destLane)
                       a maksymalnym (ilość barier zmian pasów pozostała do celu - destLane)
                       ALE! nie może wjechać na pas większy, niż na którym jest na gałęzi (o ile jest na maksymalnym pasie gałęzi)
                       */

                    int barriersLeft = minusModulo(_source, _destination, _qr->getBranches()->size() ); // liczba pozostałych okazji do zmiany pasa
                    int minimalLane = ((_destLane < _lane) ? _lane : _destLane); // wierzę, że RSim::generator nie wpuści mnie na pas 0 z destLane == 1;
                    int maximalLane = barriersLeft + _destLane;

                    maximalLane = (maximalLane >= _qr->getNumLanes() ? _qr->getNumLanes()-1 : maximalLane);
                    if (_lane < _qr->getNumLanesOnBranch(barrier->getNrBranch())-1 ) // jeśli nie jestem na zewnętrznym pasie
                        maximalLane = minimalLane;

                    int diff = minusModulo(_source, _destination, _qr->getBranches()->size());
                    // diff to różnica. Różnica = 1 oznacza, że gałęzie są obok siebie
                    if (diff == 1)
                        maximalLane = minimalLane;

                    if (minimalLane >= maximalLane)
                        _lane = minimalLane;
                    else
                        _lane = minimalLane + (qrand() % (maximalLane - minimalLane + 1));

                    prepareStopLine(_lane, barrier); // wjezdżając na dalszy pas ronda, najpierw jadę do przodu, potem skręcam. Muszę wiedzieć, kiedy się zatrzymać.
                }
                else // jazda do przodu na gałęzi
                {
                   goForward();
                   if ((minusModulo(_source, _destination, _qr->getBranches()->size()) == 1) &&
                       (_qr->getDependences()->at(_destination) == connected))
                   {
                       if ((collidesWithBarrier(shortcut) != NULL) && (_lane == 0))
                       {
                           _state = turning;
                           _lane = -2;
                       }
                   }

                }
                break;
            }
            case turning:
                enterRoundabout(); // pas został wybrany przy wywołaniu prepareStopLine();
                if ((abs(int(_angle - rotation())) >= 65) && (collidesWithBarrier(enterroundabout) == NULL))
                {
                    _angle = computeAngle();   // ustawiam prawidłowy kąt (żegnaj _angle w stopniach...)
                    _state = forward;
                    _onRim = true;
                    _onBranch = false;
                }
                break;
            case changing: // nie powinno się zdarzyć onBranch;
                break;
        }
    }

    if (_onRim)//jedzie w kółko
    {
        switch (_state)
        {
            case forward:
            {
                orbit(_qr->getRadius() - ((_qr->getLaneWidth())/2)*(((_lane)*2)+1) );
                if (_lane > _destLane)
                {
                    Barrier *barrier = collidesWithBarrier(changelane);
                    if ((barrier != _lastBarrier) &&                // jeżeli "znalazłem" tą samą barierę, co rozkminialem ostatnio, to ją ignoruję.
                        (barrier != NULL) && (_lane > _destLane))  // jeśli lane == destLane, to z tego pasu chcę już zjechać.
                    {
                        /// rozważam, czy chcę zmienić pas.
                        int barriersLeft = minusModulo(barrier->getNrBranch(), _destination, _qr->getBranches()->size()); // liczba pozostałych okazji do zmiany pasa
                        // if barriersLeft == 0, to znaczy, że albo wlasnie minalem mój cel
                        // albo, że mój wjazd przykleił się do celu - wtedy tak naprawdę mam więcej barier po drodze
                        if ((barriersLeft == 0) && (_qr->getDependences()->at(minusModulo(_destination, 1, _qr->getBranches()->size())) == glued)) // if prev == glued
                            barriersLeft = _qr->getBranches()->size() - 1;

                        if (_lane - _destLane == barriersLeft)
                        {   // ostatnia szansa na zmianę pasa - trzeba zmienić.
                            _state = changing;
                            createChangeLaneDangerZone();
                            return;
                        }
                        else
                        {   // nie muszę zmieniać pasa, ale mogę - losujmy
                            _lastBarrier = barrier;
                            int tmp = rand()%35;
                            if (tmp == 0)
                            {
                                _state = changing;
                                createChangeLaneDangerZone();
                            }
                        }
                    }
                }
                Barrier *barrier = collidesWithBarrier(exitroundabout);
                if (barrier != NULL)
                    _lastBarrier = barrier;  // to po to, by _lastBarrier działało na rondzie z jedną barierą zmiany pasa.

                if ((barrier == _destBarrier) && (_lane < _qr->getNumLanesOnBranch(barrier->getNrBranch())))
                {
                    _state = turning;
                    prepareStopLine(_lane + 0.1, barrier);
                    _angle = computeAngle(); // zapamiętujemy nasz kąt z tuż przed skręcania
                    // żeby łatwiej wykryć kiedy mamy przestać skręcać
                    _speed /= 2;  // przykro mi z tego powodu
                    _maxSpeed = _speed;

                    /* poniższa _dZ to spojrzenie w prawo, czy mogę zjechać z ronda */
                    qreal laneWidth = _qr->getLaneWidth();
                    _dangerZone = new QPainterPath(mapToParent(0.5*laneWidth, -0.5*_size));
                    _dangerZone->lineTo(mapToParent((_lane + 0.5)*laneWidth, -0.5*_size));
                    _dangerZone->lineTo(mapToParent(_lane*laneWidth, 0.5*_size));

                    return;
                }
                barrier = collidesWithBarrier(shortcutend);
                if (barrier != NULL)
                {
                    createShortcutEndDangerZone(_destination);
                    _state = turning;
                    _speed /= 2;  // przykro mi z tego powodu
                    _maxSpeed = _speed;
                    _angle = computeAngle(); // zapamiętujemy nasz kąt z tuż przed skręcania
                    // żeby łatwiej wykryć kiedy mamy przestać skręcać
                    _shortcutUsed = true;
//                    _destLane = 0;
                }
                break;
            }
            case changing:
                if (changeLane())
                    _state = forward;
                break;
            case turning:
                turnExit();

                int prev = minusModulo(_destination, 1, _qr->getBranches()->size());
                if (turnExit()) // normalizeDegAngle(rotation() - (_angle*180/Pi)) >= 80))// && (collidesWithBarrier(exitroundabout) == NULL))  //decyzja o skończeniu skręcania
                {
                    setRotation(_qr->getBranches()->at(_destination)->getFi() - 180);
                    qreal radius = QLineF(mapFromParent(0, 0), QPointF(0, 0)).length();
                    setPos(radius * cos((1.5 * Pi) - ((360-rotation())*Pi/180)), radius * sin((1.5 * Pi)  - ((360 - rotation())*Pi/180))); //ustawienie na środku gałęzi

                    if (_qr->getDependences()->at(prev) == glued)
                    { // poprzednia jest ze mną sklejona, muszę przesunąć się o jej pół szerokoścpw prawo
                        setPos(mapToParent(_qr->getBranches()->at(prev)->getWidth()/2, 0));
                    }
                    qreal myLane = _lane < 0 ? 0 : _lane;
                    // przesuwam się najpierw na zerowy pas...
                    setPos(mapToParent(_qr->getLaneWidth() * (0.5* _qr->getBranches()->at(_destination)->getNumLanes() - 0.5), 0));
                    setPos(mapToParent(-myLane * _qr->getLaneWidth(), 0)); // a teraz na mój wybrany

                    _state = forward;
                    _onRim = false;
                    _onBranch = true;
                    _destinationReached = true;
                }
                break;
        }
    }
}

/**
    Wyjazd ze skrótu na gałąź docelową.
  */
bool Car::turnExitShortcut()
{
    if (_dangerZone != NULL)
    {
        QList<QGraphicsItem *> items = scene()->items(*_dangerZone);
        items.removeOne((QGraphicsItem*)this);
        removeIgnoredCars(&items);
        foreach (QGraphicsItem* item, items)
        {
            if (Car *car = dynamic_cast<Car*>(item))
            {
                Q_UNUSED(car);
                brake();
                return false;
            }
            else if (Pedestrian *p = dynamic_cast<Pedestrian*>(item))
            {
                Q_UNUSED(p);
                delete _dangerZone;
                _dangerZone = NULL;
            }
        }
    }
    // animacja skręcania! skopiuj z turnExit;
    setRotation(normalizeDegAngle(rotation()));
    qreal diff = normalizeDegAngle(rotation() - _qr->getBranches()->at(_destination)->getFi() - 180);  // różnica między moim odchyleniem a tym , jakie mam mieć na wyjściowej gałęzi
    if (diff > 5)
    {
        /***************** SKRĘCANIE **************************/
        qreal realSpeed = goForward();
        qreal omega =  double(realSpeed) / double(1.5 * _qr->getLaneWidth());  // w radianach!
        setRotation(rotation() + omega*180.0/Pi);
        return false;  //nie skończyłem skręcać
        /*******************************************************/
    }
    else
        return true;  // skręcanie skończone
}

/**
    Opowiada za doorbitowanie do _stopLine, a następnie za wykonanie skrętu w stronę odpowiedniego pasa.
 */
bool Car::turnExit()
{
    _maxSpeed = 4;
    if (_shortcutUsed)
        return turnExitShortcut();

    //-----------Przepuszczamy wszysytkich ze strefy zagrożenia-------------
    if (_dangerZone != NULL)
    {
        QList<QGraphicsItem *> items = scene()->items(*_dangerZone);
        removeIgnoredCars(&items);
        foreach (QGraphicsItem* item, items)
        {
            if ((item->isVisible() == false) || (item == this))
                items.removeOne(item);
        }
        if (items.isEmpty() != true)
        {   // droga zajęta
            brake();
            return false;
        }
        else
        {
            delete _dangerZone;
            _dangerZone = NULL;
        }
    }

    //-----------Jedziemy naprzód aż do stopLine, bo nie zjeżdżamy na zerowy pas-------------
    if (_stopLine != NULL)
    {
        orbit(_qr->getRadius() - ((_qr->getLaneWidth())/2)*(((_lane)*2)+1)); // muszę dojechać do stopLinii
        if (collidesWithPath( mapFromParent(*_stopLine) ))
        {
            delete _stopLine;
            _stopLine = NULL;
            // dojechałem do stopLinii; w następnej klatce zacznę skręcać; ustawmy się tu prostopadle do gałęzi w którą wjeżdżam.
            setRotation(_qr->getBranches()->at(_destination)->getFi() - 180 - 75);
        }
        return false; // bo już raz w tej klatce się przesunąłem.
    }

    //-----------Skręciłem; teraz prosto w stronę gałęzi??------------------------------------------
    qreal length = QLineF(mapFromParent(0, 0), QPointF(0, 0)).length();
    if ((length >= _qr->getRadius() - 1.25*_size) && (length < _qr->getRadius()))
    { // dojechałem do pasów!!!!
        if (_dangerZone != NULL)
        { // nie muszę już zważać na inne wozy
            delete _dangerZone;
            _dangerZone = NULL;
        }
        createZebraDangerZone(_destination, _destLane);
    }
    qreal lw = _qr->getLaneWidth();
    if ((length >= _qr->getRadius() + lw/2) && (_dangerZone != NULL))
    {  // jeśli już jestem w połowie pasów
        delete _dangerZone;
        _zebraDangerZone = false;
        _dangerZone = NULL;
    }

    setRotation(normalizeDegAngle(rotation()));
    qreal diff = normalizeDegAngle(rotation() - _qr->getBranches()->at(_destination)->getFi() - 180);  // różnica między moim odchyleniem a tym , jakie mam mieć na wyjściowej gałęzi
    if (diff > 5)
    {
        /***************** SKRĘCANIE **************************/
        qreal realSpeed = goForward();
        qreal omega =  double(realSpeed) / double(1.5 * _qr->getLaneWidth());  // w radianach!
        setRotation(rotation() + omega*180.0/Pi);
        return false;  //nie skończyłem skręcać
        /*******************************************************/
    }
    else
        return true;  // skręcanie skończone
}


/**
    \brief odpowiada za animację od kolizji z barierą, do ustawienia się do ruchu okrężnego.

    Najpierw sprawdzam czy są jakieś samochody w _dangerZone, jeśli tak to hamuję (przepuszczam je).
    Jeśli ich nie ma to kasuję _dangerZone i  jadę naprzód (aż do stopLine, kiedy to zaczynam skręcać na rondo). stopLine powinna być w takim miejscu, abym po skręcaniu ustawił się na moim zadanym pasie. */
void Car::enterRoundabout()
{
    if (_dangerZone != NULL)
    {
        QList<QGraphicsItem *> items = scene()->items(*_dangerZone);
        removeIgnoredCars(&items);
        foreach (QGraphicsItem* item, items)
        {
            if ((item->isVisible() == false) || (item == this))
                items.removeOne(item);
        }
        if (items.isEmpty() != true)
        {   // droga zajęta
            brake();
            return;
        }
        else
        {
            delete _dangerZone;
            _dangerZone = NULL;
        }
    }
    
    if (_stopLine != NULL)
    {
        goForward();
        if (collidesWithPath( mapFromParent(*_stopLine) ))
        {
            delete _stopLine;
            _stopLine = NULL;
        }
        return;
    }

    qreal omega =  double(_speed) / double(1.5 * _qr->getLaneWidth());  // w radianach!
    setRotation(rotation() + omega*180.0/Pi);
    goForward();
}

/** w każdej klatce samochód który wjechał na rondo jest obracany wg wzorku: omega = _speed / promień,
  na kazdym pasie promień jest inny, najbardziej zewnętrzny pas ma promień równy promieniowi ronda _radius
  Samochód porusza się po okregu wg wzorów:
  x = x0 + promien* cos(alfa)
  y = y0 + promien* sin alfa
  alfa poczatkowo jest kątem odchylenia gałęzi a następnie w kazdej klatce dodawany jest omega
  */
void Car::orbit(qreal radius, bool accelerate)
{
    double omega = double(_speed) / double(radius);  // w radianach!

    QPainterPath path(QPointF(0, -_qr->getLaneWidth()));
    path.lineTo(-_size/2, -1.0 * _qr->getLaneWidth()); // spójrzmy przed siebie ze skrzywieniem na lewo

    QList<QGraphicsItem *> items = scene()->items(mapToParent(path)); // czy coś jest na drodze do mojego najbliższego punktu?
    removeIgnoredCars(&items);
    foreach (QGraphicsItem* item, items)
    {
        if ((item->isVisible() == false) || (item == this))
            items.removeOne(item);
    }
    if (items.isEmpty() )
    { // no to jadziem
//        if ((_minSpeed > 0) && (_state != changing)) // to sugeruje, że jestem irritated (lub że zmieniam pas... )
//            brake();

       radius += _laneShift;
       _angle -= omega;
       setPos(radius * cos(_angle), radius * sin(_angle));  // orbitujmy! hihi :D

       if (_speed < _maxSpeed)
           if (accelerate)  _speed += 0.2;

       // obracanie samochodu stosowne do pozycji
       qreal B = QLineF(mapFromParent(0, -radius), QPointF(0, 0)).length();
       qreal A = QLineF(mapFromParent(0, 0), QPointF(0, 0)).length();
       qreal C = QLineF(mapFromParent(0, 0), mapFromParent(0, -radius)).length();

       qreal cosdeviation = (pow(B, 2) - pow(A, 2) - pow(C, 2)) / -(2 * A * C);
       qreal deviation = acos(cosdeviation);
       if (x() > 0)
           deviation = 360 - 90 + (deviation * 180) / Pi;
       else
           deviation = 360 - (90 + (deviation * 180) / Pi);
       setRotation(deviation);
    }
    else
        brake();
}
/** @brief Dostoswuje prędkość do warunków określonych przez _minSpeed i _maxSpeed. */
void Car::brake()
{
    _speed -= 0.3;
    if (_speed < _minSpeed)
        _speed = _minSpeed;
}

/**
    \brief To, co tygryski lubią najbardziej, czyli mkniemy naprzód.

    Proste pojechanie naprzód, niestety nie jest takie proste. W pierwszej kolejności, trzeba skonstruować obiekt QPainterPath przed naszym nosem. Przez sprawdzenie, czy ta ścieżka koliduje z innymi obiektami, dowiadujemy się czy droga przed nami jest wolna.
    Następnie sprawdzamy, czy nie znajdujemy się w specjalnym przypadku zjeżdżania ronda (innymi słowy: nasz przód jest bardziej oddalony od centrum ronda, niż nasz środek). Jeśli to prawda, to należy stwożyć strefę zagrożenia, ponieważ być może zbliżamy się do pasów. Innym charakterystycznym przypadkiem jest moment, w którym minęliśmy już pasy - należy wtedy usunąć tą strefę zagrożenia, abyśmy nie zatrzymywali się bez sensu, gdy ktoś za nami wejdzie na pasy.

    Mając to już z głowy, sprawdzamy, czy aktualnie istnieje jakaś strefa zagrożenia. Jeśli tak - sprawdzamy, czy droga wolna. Jeśli nie, sprawdzamy czy nie mamy czegoś tuż przed maską. Jeśli jest tam pusto, przesuwamy się do przodu.
  */
qreal Car::goForward()
{ 
    QPainterPath path(QPointF(0, -_qr->getLaneWidth()));
    // spójrzmy przed siebie ze skrzywieniem na prawo (żeby przy skręcaniu nie wykrywać sąsiednich pasow)
    path.lineTo(+_size/2, -1.5 * _qr->getLaneWidth());
    path.lineTo(+_size/2, -_qr->getLaneWidth());

    qreal length = QLineF(mapFromParent(0, 0), QPointF(0, 0)).length(); // odległość ode mnie, do środka ronda
    qreal frontLength = QLineF(mapFromParent(0, 0), QPointF(0, 1)).length();    // odległość od mojego przodu, do środka ronda
    qreal lw = _qr->getLaneWidth();     // szerokość pasa
    qreal r = _qr->getRadius();         // promień ronda

    if ((_state == forward) && (frontLength > length) && (length < r+2*lw) && (_dangerZone == NULL))
    {
        // muszę: jechać prosto, zbliżać się do ronda, być w odpowiedniej odległości do pasów
        createZebraDangerZone(_source, _lane);
    }
    if ((length < r+1.2*lw) && (_dangerZone == NULL) && (_state == forward))
    {
        if (_dangerZone != NULL)
        {
            delete _dangerZone;
            _dangerZone = NULL;
        }
    }

    if (_dangerZone != NULL)
    {
        QList<QGraphicsItem *> items = scene()->items(*_dangerZone);
        removeIgnoredCars(&items);
        foreach (QGraphicsItem* item, items)
        {
            if ((item->isVisible() == false) || (item == this))
                items.removeOne(item);
            if (_zebraDangerZone) // chodzi nam tylko o pieszych, w samochody nie wjedziemy i tak, bo szperacz
            {
                 if (Car *car= dynamic_cast<Car*>(item))
                 {
                     Q_UNUSED(car);
                     items.removeOne(item);
                 }
            }
        }
        if (items.isEmpty() != true)
        {   // droga zajęta
            brake();
        }
        else
        {
            delete _dangerZone;
            _dangerZone = NULL;
        }
    }

    QList<QGraphicsItem *> items = scene()->items(mapToParent(path));
    removeIgnoredCars(&items);
    foreach (QGraphicsItem* item, items)
    {
        if ((item->isVisible() == false) || (item == this))
            items.removeOne(item);
    }
    if ((items.isEmpty()) || (_secondsOfIrritation > 5))
    {
       setPos(mapToParent(0, -_speed));
       if (_speed < _maxSpeed)
           _speed += 0.2;
       return (_speed);
    }
    else
    {   // coś jest przede mną!
        brake();
        return 0;
    }
}

/** \brief Ustawiam _angle na zgodny z aktualną pozycją pos().

    UWAGA! Zwracany angle jest w RADIANACH!
    To zapewnia w miarę płynne połączenie między skrętem samochodu, a jego orbitowaniem.
    UWAGA: obliczony kąt różni się od aktualnego rotation o 270 stopni. W sensie deg(_angle) = rotation() - 270. Jest to związane z konwencją Qt. */
qreal Car::computeAngle()
{
    qreal radius = _qr->getRadius();
    qreal B = QLineF(mapFromParent(0, -radius), QPointF(0, 0)).length();
    qreal A = QLineF(mapFromParent(0, 0), QPointF(0, 0)).length();
    qreal C = QLineF(mapFromParent(0, 0), mapFromParent(0, -radius)).length();

    qreal cosdeviation = (pow(B, 2) - pow(A, 2) - pow(C, 2)) / -(2 * A * C);
    qreal deviation = acos(cosdeviation); //to są radiany

    qreal angle = 1.5 * Pi - deviation;

    if (x() > 0)
    {
        angle += Pi;
        angle = -angle;
    }

    angle = normalizeAngle(angle);
    return angle;
}


/**
    Zwraca barierę zjazdu powiązaną z danym numerem gałęzi.
  */
Barrier* Car::findOutletBarrier(int dest)
{
    int i = -1; // iterator po wszystkich barierach
    int nrOutletBarrier = -1;  // iterator po barierach zjazdu

    foreach(Branch *b, *(_qr->getBranches()))
    {
        i++;
        if (b->getDirection() == entry)
            continue;
        else
            nrOutletBarrier++;
        if (dest == i)
            break;
    }
    //majac numer zjazdu zwracam barierę, z ktora powinna byc kolizja zeby samochod zjechal
    if (nrOutletBarrier < 0)
        return _qr->getOutletBarriers()->at(0);
    else
    {
        //qDebug()<<"zjazd " <<roundabout->_outletBarriers[cel]->getNrBranch();
        return _qr->getOutletBarriers()->at(nrOutletBarrier);
    }
}


/** @brief funkcja odpowiada za animację zmieniania pasa.

    Uwzględnia _dangerZone, ale jeżeli kogoś tam wykryje to nie zatrzymuje wozu, tylko każe mu się powoli czołgać (bo rzadko na rondzie ktoś całkiem staje)
    Animację wykonuje modyfikując zmiennę _laneShift, która jest wykorzystywana w orbit().
    Jeżeli samochód już się wepchał na drugi pas (pokonał 1/3 drogi), to przestaje zważać na _dangerZone.
    @return false, jeżeli nie zakończył zmieniać pasa
    @return true, jeśli skończył */
bool Car::changeLane()
{
    if (_dangerZone != NULL)
    {
        QList<QGraphicsItem *> items = scene()->items(*_dangerZone);
        removeIgnoredCars(&items);
        foreach (QGraphicsItem* item, items)
        {
            if ((item->isVisible() == false) || (item == this))
                items.removeOne(item);
        }
        if ((items.isEmpty()) || (_laneShift > _qr->getLaneWidth()/2))
        { // drugi warunek: bo nie będziemy hamować jak juz jesteśmy na drugim pasie
            _maxSpeed = 4;
            _laneShift += (double(_qr->getLaneWidth()) / 10) * (_speed / _maxSpeed);
            if ( _laneShift < double(_qr->getLaneWidth()) )
            {
                orbit(_qr->getRadius() - ((_qr->getLaneWidth())/2)*(((_lane)*2)+1));
                return 0;
            }
            else    // zjechałem na drugi pas.
            {
                _lane -= 1;
                _laneShift = 0;
                delete _dangerZone;
                _dangerZone = NULL;
                _maxSpeed  = 4;
                return 1;
            }
        }
        else    // ktoś jest w _dZ
        {
            _maxSpeed = 0.3; // lekko się pchamy, w końcu ktoś nas wpuści
            brake();
            orbit(_qr->getRadius() - ((_qr->getLaneWidth())/2)*(((_lane)*2)+1), false); //
            createChangeLaneDangerZone();
            return 0;
        }
    }
    else    // _dZ == NULL
    {
        _maxSpeed = 4;
        _laneShift += double(_qr->getLaneWidth()) / 10;
        if ( _laneShift < double(_qr->getLaneWidth()) )
        {
            orbit(_qr->getRadius() - ((_qr->getLaneWidth())/2)*(((_lane)*2)+1));
            return 0;
        }
        else
        {
            _lane -= 1;
            _laneShift = 0;
            _maxSpeed = 4;
            return 1;
        }
    }
    return 0;
}

/** Wywoływana zarówno przy kolizji z barierą enterroundabout jak exitroundabout.
  wyznacza linię, do której dojedzie samochód, zanim skręci na swój pas na rondzie lub gałęzi.
  Zapisuje ją w zmiennej _stopLine, potem podczas skręcania (turnExit() lub enterRoundabout())
  sprawdzam, czy dojechałem już do _stopLine.
  Poza tym oblicza _dangerZone dla wjazdu na rondo (stąd konieczność drugiego argumentu). */
void Car::prepareStopLine(double lane, Barrier* barrier)
{
    if (barrier->getBarrierType() == enterroundabout)
    {
        /* dz musy być równoległobokiem. Pierwsze dwa punkty dobrze policzyłem
czwarty też jest dobrze, został trzeci.*/
        lane = int(lane);
        int numLanes = _qr->getBranches()->at(barrier->getNrBranch())->getNumLanes();
        double f = ( 1.0 - (double(numLanes - (lane + 1)) / double(numLanes)) )* 11.0 + 30.0;  // to, żeby gość na prawym pasie patrzył krócej, niż ten na lewym. Dwie ostatnie liczby w tej linijce są magicznym wynikiem obserwacji i eksperymentów.
        double alfa = (rotation() + 90 + f) * Pi / 180;
        QPointF startPoint(mapToParent(0,-_size));
        QPointF P1(mapToParent(0, -double(double(_qr->getLaneWidth() * (lane+1)))));
        double radius = _qr->getRadius() - _qr->getLaneWidth()/2; // ten minus, żeby nie uwzględnić wjeżdżających samochodów za bardzo
        QPointF P2(radius * cos(alfa), radius * sin(alfa));

        /* startPoint to punkt tuż przed samochodem
           P1 to środek pasa na który jedziemy, na wprost od samochodu
           P2 to kąt równoległoboku na zewnętrznym pasie ronda
           P3 to kąt na pasie ronda na który jedziemy.
           */
        QPointF P3 = P1 - (startPoint - P2);

        /* Zauważyłem, że trzeba jeszcze zerkać na pas docelowy w prawo
           żeby nie wbić się w korek. Więc po P1, trzeba jeszcze pójść w prawo (lookRight)

           Kolejna sprawa: jadąc "po prostu" z P1 do P3, to mamy linię prostą
           która może haczyć o inne wewnętrzniejsze pasy.
           Zatem dodałem punkt waypoint, to jest ordynarny look w lewo na długość
           szerokości mojej ulicy na lewo ode mnie.

           Cześć, to znowu ja
           Otóż nie musimy przecież patrzeć na pas zerowy, bo wszakże
           z tego pasa się zjeżdża, więc samochód tam do nas nie dojedzie.
           Zatem dodamy do P2 tuż przed narysowaniem _laneWidth do składowej igrek.

           Cześć, to jeszcze raz ja.
           Chciałem tylko powiedzieć, że wszystko działa dobrze, pomimo wątpliwej jakości
           powyższych dywagacji (niektórzy z zerowego pasa nie zjeżdżają...).
          */
        QPointF lookRight(mapToParent(2*_size, -_qr->getLaneWidth() * (lane+1)));
        QPointF waypoint(mapToParent(-_qr->getLaneWidth() *double(numLanes - _lane - 0.5), -_qr->getLaneWidth() * (lane+1)));
        QPointF tmpP2(mapFromParent(P2.x(), P2.y()));
        tmpP2.setY(tmpP2.y() - _qr->getLaneWidth());
        P2 = mapToParent(tmpP2);

        _dangerZone = new QPainterPath(startPoint);
        _dangerZone->lineTo(P1);
        _dangerZone->lineTo(lookRight);
        _dangerZone->lineTo(waypoint);
        _dangerZone->lineTo(P3);
        _dangerZone->lineTo(P2);
    }

    if (lane == 0)
        return;

    QPointF *P1 = new QPointF(barrier->line().p1());
    QPointF *P2 = new QPointF(barrier->line().p2());
    if (barrier->getBarrierType() == enterroundabout)
        movePoints(P1, P2, -lane * _qr->getLaneWidth());
    else
        movePoints(P1, P2, lane * _qr->getLaneWidth());

    _stopLine = new QPainterPath(*P1);
    _stopLine->lineTo(*P2);
}

/** @brief Zwraca adres bariery (o zadanym typie), z którą jest kolizja.
    @param type typ bariery, jeden z \link types.h::BarrierType enuma BarrierType \endlink
    @return adres bariery, lub NULL jeśli nie zachodzi kolizja. */
Barrier* Car::collidesWithBarrier(BarrierType type)
{
    Barrier* result = NULL;
    switch (type)
    {
        case enterroundabout:
            foreach(Barrier *barrier, *(_qr->getEntryBarriers()))
            {
                if (collidesWithLine(barrier->line()))
                     result = barrier;
            }
            break;
        case exitroundabout:
            foreach(Barrier *barrier, *(_qr->getOutletBarriers()))
            {
                 if (collidesWithLine(barrier->line()))
                     result = barrier;
            }
            break;
        case changelane:
            foreach(Barrier *barrier, *(_qr->getChangeLaneBarriers()))
            {
                 if (collidesWithLine(barrier->line()))
                     result = barrier;
            }
            break;
        case shortcut:
            foreach(Barrier *barrier, *(_qr->getShortcutBarriers()))
            {
                 if (collidesWithLine(barrier->line()))
                     result = barrier;
            }
            break;
        case shortcutend:
            foreach(Barrier *barrier, *(_qr->getShortcutEndBarriers()))
            {
                 if (collidesWithLine(barrier->line()))
                     result = barrier;
            }
            break;
    }
    return result;
}

/** @brief Uproszczony algorytm wykrywania kolizji między samochodem a odcinkiem AB.

    Działa szybciej niż rozwiązania w Qt, prawdopodobnie dlatego, że oni i tak potem traktują linie jak QPainterPaths.
    CCW() zdefiniowane w \link types.cpp::CCW() types.cpp \endlink
    Zmienne wykorzystywane do obliczeń są zmiennymi klasowymi obliczonymi w konstruktorze. */
bool Car::collidesWithLine(QLineF line)
{   // CCW jest zdefiniowane w types.cpp
    QPointF A = mapFromParent(line.p1());
    QPointF B = mapFromParent(line.p2());
    // z linią (upperLeft,upperRight)
    if ((CCW(A,upperLeft,upperRight) != CCW(B,upperLeft,upperRight)) && (CCW(A,B,upperLeft) != CCW(A,B,upperRight)))
        return true;
    // z linią (upperLeft,bottomLeft)
    if ((CCW(A,upperLeft,bottomLeft) != CCW(B,upperLeft,bottomLeft)) && (CCW(A,B,upperLeft) != CCW(A,B,bottomLeft)))
        return true;
    // z linią (bottomRight,bottomLeft)
    if ((CCW(A,bottomRight,bottomLeft) != CCW(B,bottomRight,bottomLeft)) && (CCW(A,B,bottomRight) != CCW(A,B,bottomLeft)))
        return true;
    // z linią (upperRight,bottomRight)
    if ((CCW(A,upperRight,bottomRight) != CCW(B,upperRight,bottomRight)) && (CCW(A,B,upperRight) != CCW(A,B,bottomRight)))
        return true;

    // jak z żadną się nie przecinam to false
    return false;
}

/**
    Strefa zagrożenia dla samochodów wjeżdżających ze skrótu na gałąź docelową. Będą oni obserwować czy coś nie nadjeżdża od strony pasów.
  */
void Car::createShortcutEndDangerZone(int nrBranch)
{
    // w sumie... czy to nie to samo co createZebraDangerZone na pasie zero?
    // sprawdźmy
    createZebraDangerZone(nrBranch, 0);
}

/**
  \brief Tworzy strefę zagrożenia na pasach na naszym pasie.

  Zawarte tu obliczenia mogą nie być do końca jasne, ale sprowadza się to do narysowania prostokątu na zadanej gałęzi i wybranym pasie, który obejmie pasy dla pieszych.
  */
void Car::createZebraDangerZone(int nrBranch, int nrLane)
{
    // i tu trzeba obliczyć _dZ w postaci wycinka mojego pasa długiego na _laneWidth.
    qreal dist = _qr->getRadius();
    Branch *b = _qr->getBranches()->at(nrBranch);
    qreal fi = b->getFi();
    int numLanesOnBranch = b->getNumLanes();
    if (_qr->getDependences()->at(nrBranch) == glued)
    {
        fi = _qr->getBranches()->at(plusModulo(nrBranch, 1, _qr->getBranches()->size()))->getFi();
        numLanesOnBranch += _qr->getBranches()->at(plusModulo(nrBranch, 1, _qr->getBranches()->size()))->getNumLanes();
    }

    QPointF X(-dist * sin(fi*PI/180), dist * cos(fi*PI/180));  // punkt na środku wybranej gałęzi, dokładnie w odległości promienia ronda od środka ronda.
    QPointF wektor(int(-dist * sin(fi*PI/180)), int(dist * cos(fi*PI/180)));

    qreal length = sqrt(pow(wektor.x(), 2) + pow(wektor.y(), 2));
    wektor /= length;    //tak, to tak działa

    QPointF vWektor = wektor;
    // obracam o 90 st.
    qreal tmp = wektor.x();
    wektor.setX(-wektor.y());
    wektor.setY(tmp);

    if (_onBranch) // jeśli jestem na gałęzi, to znaczy że strefę muszę narysować w drugą stronę: w stronę ronda.
        wektor *= -1;

    qreal lw = _qr->getLaneWidth();
    int prev = minusModulo(nrBranch, 1, _qr->getBranches()->size());
    if (_qr->getDependences()->at(prev) == glued)
    { // poprzednia jest ze mną sklejona, muszę przesunąć się o jej pół szerokości w prawo
        X = X + wektor * (_qr->getBranches()->at(prev)->getWidth()/2);
    }
    // przesuwam się na zerowy pas
     X += wektor * (lw * (0.5* numLanesOnBranch - 0.5));
     X += wektor * (-nrLane * lw); // a teraz na mój wybrany

     // Teraz X jest na środku mojego pasa
     _dangerZone = new QPainterPath(QPointF(X - wektor*(lw/2)));
     _dangerZone->lineTo(QPointF(X + wektor*(lw/2)));
     _dangerZone->lineTo(QPointF(X + wektor*(lw/2) + vWektor*lw));
     _dangerZone->lineTo(QPointF(X - wektor*(lw/2) + vWektor*lw));

     _zebraDangerZone = true;
}

/** @brief Tworzy strefę zagrożenia dla zmiany pasa.

    _dangerZone przyjmuje kształt linii wzdłuż samochodu na sąsiednim pasie na prawo. */
void Car::createChangeLaneDangerZone()
{
    if (_dangerZone != NULL)
        delete _dangerZone;

    _dangerZone = new QPainterPath(mapToParent(1.00*_size, 1*_size));
    _dangerZone->lineTo(mapToParent(1.00*_size, - 2.75*_size));
    _dangerZone->lineTo(mapToParent(0.75*_size, - 2.75*_size));
}

/** Kliknięcie PPM na samochód zatrzymuje go bezwzględnie */
void Car::mousePressEvent ( QGraphicsSceneMouseEvent * event )
{
    if (event->button() == Qt::LeftButton)
    {
        QList<QGraphicsItem *> items = scene()->items(mapToParent(*_car));
        foreach (QGraphicsItem* item, items)
        {
            if (Car *car = dynamic_cast<Car*>(item))
                if (car != this)
                    _ignoredCars << car;
        }
        if (_dangerZone != NULL)
        {
            delete _dangerZone;
            _dangerZone = NULL;
        }
    }
    else
        _disabled = !_disabled;
}

void Car::hoverEnterEvent ( QGraphicsSceneHoverEvent * event )
{
    QString rules;
    switch (_rules)
    {
    case 0:
        rules = "oficjalne";
        break;
    case 1:
        rules = tr("odpowiadające pasy");
        break;
    case 2:
        rules = tr("odwrócone pierwszeństwo");
        break;
    default:
        rules = "brak";
        break;
    }

    QString state;
    switch (_state)
    {
    case 0:
        state = "forward";
        break;
    case 1:
        state = tr("turning");
        break;
    case 2:
        state = tr("changing");
        break;
    default:
        state = "brak";
        break;
    }

    QString tip = "Cel: " + QString::number(_destination) + " \nPas docelowy: " + QString::number(_destLane) +
                  tr("\nŹródło: ") + QString::number(_source) + "\nPas: " + QString::number(_lane) +
                  tr("\nPrędkość: ") + QString::number(_speed) + "\nmaxSpeed: " + QString::number(_maxSpeed) +
                  tr("\nminSpeed: ") + QString::number(_minSpeed) + "\nzasady: " + rules + "\nstan: " + state +
                  tr("\nileIgnorowanych: ") + QString::number(_ignoredCars.size());
    setToolTip(tip);
}

void Car::removeIgnoredCars(QList<QGraphicsItem*> *list)
{
    foreach (Car* car, _ignoredCars)
        list->removeOne(car);
}

/** Wysyłamy sygnał, dzięki któremu można liczyć ile obecnie samochodów jest na rondzie.
 */
Car::~Car()
{
    emit(iAmDestroyed());
}
